---
title: SAIF.Business.Utilities business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-08-21
library: SAIF.Business.Utilities
reference_type: embedded
---

# SAIF.Business.Utilities business rules

This page captures business rules enforced by this library.

## BR-001 Null or whitespace string

| Field | Value |
| --- | --- |
| **Statement** | A `Nothing` or whitespace-only string is returned unchanged by title casing |
| **Trigger** | `ToTitleCase` invoked on `Nothing` or string that trims to zero length |
| **Enforcement** | `SAIF.Business.Utilities/TitleCase.vb` — `ToTitleCase` guard clause checks `String.IsNullOrEmpty(inString.Trim)` |
| **Outcome** | Input returned as-is; no exception is raised |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-002 Delimiter-only input

| Field | Value |
| --- | --- |
| **Statement** | If the input contains no word characters (only delimiters), the original string is preserved |
| **Trigger** | `ToTitleCase` after regex split produces no non-blank tokens |
| **Enforcement** | `TitleCase.vb` — `ToTitleCase` method checks `If SplitWords.Any(Function(w) Not String.IsNullOrWhiteSpace(w))` |
| **Outcome** | Original string retained without modification |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-003 Delimiter preservation

| Field | Value |
| --- | --- |
| **Statement** | All original non-alphanumeric delimiter characters (including leading ones and repeated runs such as `---`) must be preserved in their original positions |
| **Trigger** | Reassembly of cased words after per-word formatting |
| **Enforcement** | `TitleCase.vb` — `ReassembleString` (both overloads) reconstructs the output by mapping word positions back to source character indices |
| **Outcome** | Output has identical punctuation and spacing layout to input |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-004 Preformatted mixed-case

| Field | Value |
| --- | --- |
| **Statement** | A word that already contains both upper- and lower-case characters is treated as intentionally cased and is not reformatted |
| **Trigger** | Per-word casing rule evaluation, first branch |
| **Enforcement** | `TitleCase.vb` — `TitleCaseSingleWord` checks if `word.Any(Char.IsUpper) AndAlso word.Any(Char.IsLower)` |
| **Outcome** | Word returned unchanged, bypassing all other casing rules |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-005 Acronyms and abbreviations

| Field | Value |
| --- | --- |
| **Statement** | Recognized acronyms and abbreviations (e.g. `SAIF`, `LLC`, `LLP`, `MD`, `PhD`, directionals `NE/NW/SE/SW`, state codes `OR/CA/WA`, roman numerals `II/III/IV`, organization codes such as `OHSU`, `MCO`, `AAA`) are always fully upper-cased |
| **Trigger** | Per-word casing evaluation; word's uppercase form matches an entry in `UpperCaseWordList` |
| **Enforcement** | `TitleCase.vb` — `TitleCaseSingleWord` checks `If word.ToUpper() IsIn UpperCaseWordList` |
| **Outcome** | Word is fully upper-cased (e.g. `llc` → `LLC`) |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-006 Minor words

| Field | Value |
| --- | --- |
| **Statement** | Recognized minor words (e.g. `is`, `in`, `the`, `an`, `of`, `on`, `and`, `de`) are lower-cased |
| **Trigger** | Per-word casing evaluation; word's lowercase form matches an entry in `LowerCaseWordList` |
| **Enforcement** | `TitleCase.vb` — `TitleCaseSingleWord` checks `If word.ToLower() IsIn LowerCaseWordList` |
| **Outcome** | Word is fully lower-cased (unless it is the first character — see [BR-011](business-rules.md#br-011-first-character-override)) |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-007 Irregular mixed-case spelling

| Field | Value |
| --- | --- |
| **Statement** | Words with required irregular casing (e.g. `PhD`, `LaCombe`) are replaced with their canonical spelling regardless of input casing |
| **Trigger** | Per-word casing evaluation; word's lowercase form matches (case-insensitive) an entry in `MixedCaseWordList` |
| **Enforcement** | `TitleCase.vb` — `TitleCaseSingleWord` checks if `word.ToLower()` matches a `MixedCaseWordList` entry and replaces with the canonical form |
| **Outcome** | `PHD` → `PhD`; `lacombe` → `LaCombe` |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-008 Ordinal suffix normalization

| Field | Value |
| --- | --- |
| **Statement** | An ordinal token consisting solely of digits followed by `st`/`nd`/`rd`/`th` keeps the digits and lower-cases the suffix |
| **Trigger** | Per-word casing evaluation; word matches the ordinal pattern (numeric part + suffix) |
| **Enforcement** | `TitleCase.vb` — `HasNumberSuffix` validates the token via `ContainsOnlyNumericCharacters`; `GetNumberSuffixParts` splits the numeric and suffix parts, which are rejoined with the suffix lowered |
| **Outcome** | `52ND` → `52nd`; `A52ND` is not purely numeric so falls through to [BR-009](business-rules.md#br-009-default-proper-casing) |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-009 Default proper-casing

| Field | Value |
| --- | --- |
| **Statement** | Any word not matched by a specific list rule (BR-005, BR-006, BR-007, BR-008) is proper-cased (first letter upper, remainder lower) |
| **Trigger** | Per-word casing evaluation; all prior branches did not match |
| **Enforcement** | `TitleCase.vb` — `TitleCaseSingleWord` `Else` branch calls `ToProperCase` via `StrConv(..., VbStrConv.ProperCase)` |
| **Outcome** | `MANAGEMENT` → `Management`; `jUnGLe` → `Jungle` |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-010 Trailing possessive suffix

| Field | Value |
| --- | --- |
| **Statement** | A trailing possessive or exact suffix (`'s`) is normalized to its canonical lower-case form |
| **Trigger** | After reassembly, per space-delimited word within the final result |
| **Enforcement** | `TitleCase.vb` — `ApplyExactSuffixList` scans `MixedCaseSuffixList` and replaces matched suffixes with their canonical form |
| **Outcome** | `MIKE'S` → `Mike's`; `JAMES'S` → `James's` (or `James'` depending on list entry) |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-011 First character override

| Field | Value |
| --- | --- |
| **Statement** | The first character of the final title-cased result is always upper-cased, overriding any minor-word rule |
| **Trigger** | Final step of `ToTitleCase` method |
| **Enforcement** | `TitleCase.vb` — `ToTitleCase` return statement forces the first character to upper case: `.First().ToString().ToUpper() + ...` |
| **Outcome** | `this is a test` → `This is a test` (not `this Is A Test` if "This" were in the minor-word list) |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-012 Surname prefixes

| Field | Value |
| --- | --- |
| **Statement** | Surname prefixes `Mc` and `O'` retain their casing and the following segment is proper-cased |
| **Trigger** | After primary per-word casing; word starts with a `MixedCasePrefixList` entry |
| **Enforcement** | `TitleCase.vb` — `TitleCaseSingleWord` contains prefix handling logic that checks if the word starts with `Mc` or `O'`, preserves the prefix, and proper-cases the remainder |
| **Outcome** | `MCMAHON` → `McMahon`; `GREG O'BRIEN` → `Greg O'Brien` |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## BR-013 Number magnitude limit

| Field | Value |
| --- | --- |
| **Statement** | Numbers greater than 999,999,999,999,999 cannot be converted to words |
| **Trigger** | `NumberToText` invoked with `n > 999999999999999` |
| **Enforcement** | `SAIF.Business.Utilities/mdlTextConversion.vb` — first guard in `NumberToText` throws `ApplicationException` |
| **Outcome** | `ApplicationException("Number too large to convert to text.  Limit is 999,999,999,999,999.")` is raised |
| **Source** | `SAIF.Business.Utilities/mdlTextConversion.vb` |

---

## BR-014 Negative number rendering

| Field | Value |
| --- | --- |
| **Statement** | Negative numbers are rendered with a leading `Minus ` prefix followed by the absolute-value wording |
| **Trigger** | `NumberToText` invoked with `n < 0` |
| **Enforcement** | `mdlTextConversion.vb` — `NumberToText` checks `If n < 0` and returns `"Minus " + NumberToText(-n)` |
| **Outcome** | `-5` → `Minus Five `; `-123` → `Minus One Hundred Twenty Three ` |
| **Source** | `SAIF.Business.Utilities/mdlTextConversion.vb` |

---

## BR-015 Zero empty string

| Field | Value |
| --- | --- |
| **Statement** | Zero converts to an empty string, not the word "Zero"; this also terminates recursion for exact multiples |
| **Trigger** | `NumberToText` invoked with `n = 0` |
| **Enforcement** | `mdlTextConversion.vb` — `NumberToText` checks `If n = 0` and returns an empty string (not `"Zero"`) |
| **Outcome** | Empty string returned; also terminates recursion for exact multiples (e.g. `1000` terminates after "One Thousand" with no trailing "Zero") |
| **Source** | `SAIF.Business.Utilities/mdlTextConversion.vb` |

---

## BR-016 Magnitude band decomposition

| Field | Value |
| --- | --- |
| **Statement** | Numbers are decomposed by magnitude band (hundred, thousand, million, billion, trillion) with the leading `1` band emitted as the literal "One <band>" |
| **Trigger** | `NumberToText` recursion for values >= 100 |
| **Enforcement** | `mdlTextConversion.vb` — `NumberToText` contains branches for each magnitude band; the function recurses on `RoundDown(n, band)` to extract the band's value, appends the band name, and recurses on `n Mod band` for the remainder |
| **Outcome** | Word sequence assembled from highest band down (e.g. `1234` → `One Thousand Two Hundred Thirty Four `) |
| **Source** | `SAIF.Business.Utilities/mdlTextConversion.vb` |

---

## BR-017 Sentence case normalization

| Field | Value |
| --- | --- |
| **Statement** | Sentence casing upper-cases only the first character and lower-cases the rest; `Nothing` passes through as `Nothing` and an empty string is unchanged |
| **Trigger** | `ToSentenceCase` invoked |
| **Enforcement** | `mdlTextConversion.vb` — `ToSentenceCase` method contains `Select Case Target.Length` branches; when length > 1, it returns the first character upper-cased plus the remainder lower-cased |
| **Outcome** | `hELLO wORLD` → `Hello world`; `Nothing` → `Nothing`; empty string → empty string |
| **Source** | `SAIF.Business.Utilities/mdlTextConversion.vb` |

---

## BR-018 Capital-padding skip

| Field | Value |
| --- | --- |
| **Statement** | Capital-letter padding is skipped for any value that already contains a space |
| **Trigger** | `ToPadCapitalLetters` invoked on a string |
| **Enforcement** | `TitleCase.vb` — `TitleCaseExtensions.ToPadCapitalLetters` checks `If Target.Contains(" ")` and returns the value unchanged if true; otherwise delegates to `PadCapitalLettersFormat.ApplyFormat` |
| **Outcome** | Value returned unchanged if it already contains a space; otherwise `PadCapitalLettersFormat.ApplyFormat` is applied |
| **Source** | `SAIF.Business.Utilities/TitleCase.vb` |

---

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [API reference](api/index.md)

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

## Sources
- `SAIF.Business.Utilities/TitleCase.vb`
- `SAIF.Business.Utilities/mdlTextConversion.vb`
